﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace RegexTools
{
    public static class Extensions
    {
        public static string Slice(this string str, int? start = null, int? end = null, int step = 1)
        {
            if (step == 0) throw new ArgumentException("Step size cannot be zero.", "step");

            if (start == null) start = step > 0 ? 0 : str.Length - 1;
            else if (start < 0) start = start < -str.Length ? 0 : str.Length + start;
            else if (start > str.Length) start = str.Length;

            if (end == null) end = step > 0 ? str.Length : -1;
            else if (end < 0) end = end < -str.Length ? 0 : str.Length + end;
            else if (end > str.Length) end = str.Length;

            if (start == end || start < end && step < 0 || start > end && step > 0) return "";
            if (start < end && step == 1) return str.Substring(start.Value, (end - start).Value);

            var sb = new StringBuilder((int)Math.Ceiling((end - start).Value / (float)step));
            for (int i = start.Value; step > 0 && i < end || step < 0 && i > end; i += step)
                sb.Append(str[i]);
            return sb.ToString();
        }

        public static char? Get(this string str, int index)
        {
            if (index < 0) index += str.Length;
            if (index >= 0 && index < str.Length) return str[index];
            return null;
        }

        public static bool IsEscaped(this string str, int index)
        {
            bool escaped = false;
            while (index > 0 && str[--index] == '\\') escaped = !escaped;
            return escaped;
        }

        public static int IndexOfUnescaped(this string str, char ch, int start = 0)
        {
            for (int i = start; i < str.Length; ++i)
                if (str[i] == ch && !str.IsEscaped(i))
                    return i;
            return -1;
        }

        public static int LastIndexOfUnescaped(this string str, char ch)
        {
            return LastIndexOfUnescaped(str, ch, str.Length - 1);
        }

        public static int LastIndexOfUnescaped(this string str, char ch, int start)
        {
            for (int i = start; i >= 0; --i)
                if (str[i] == ch && !str.IsEscaped(i))
                    return i;
            return -1;
        }

        public static IEnumerable<string> SplitUnescaped(this string str, params char[] chars)
        {
            int start = 0;
            for (int i = 0; i < str.Length; ++i)
            {
                if (!chars.Contains(str[i]) || str.IsEscaped(i)) continue;
                yield return str.Substring(start, i - start);
                start = i + 1;
            }
            yield return str.Substring(start);
        }

        public static void AddRange<T>(this IList<T> list, IEnumerable<T> items)
        {
            if (list == null) throw new ArgumentNullException("list");
            if (items == null) throw new ArgumentNullException("items");
            foreach (T item in items) list.Add(item);
        }

        public static T Pop<T>(this IList<T> list)
        {
            if (list == null) throw new ArgumentNullException("list");
            int index = list.Count - 1;
            var item = list[index];
            list.RemoveAt(index);
            return item;
        }

        public static string Repeat(this string value, int count)
        {
            return new StringBuilder(value.Length * count).Insert(0, value, count).ToString();
        }

        private static readonly Random rand = new Random();
        public static T Random<T>(this IEnumerable<T> e)
        {
            return e.ElementAt(rand.Next(e.Count()));
        }
    }
}
